use alloc::boxed::Box;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use core::mem::size_of;
use core::mem::size_of_val;
use core::slice::from_raw_parts;
use embed_std::sync::channel::{sync_channel, Receiver, SyncSender};
use embed_std::{debugln, errorln, infoln};

const CALL_MAGIC: &[u8; 4] = b"rpc_";

pub type CallReturn = Result<Vec<u8>, String>;

pub struct Message {
    pub topic: String,
    /// content[0..4] == rpc_, this is call message. [4..] is Box<CallMessage>, need to free
    pub content: Vec<u8>,
}

impl Drop for Message {
    fn drop(&mut self) {
        // infoln!("drop Message");
        if let Some(p) = self.call_data() {
            let data = unsafe { Box::from_raw(p) };
            drop(data);
        }
    }
}

impl Message {
    pub fn new(topic: &str, content: &[u8]) -> Self {
        Message {
            topic: topic.to_string(),
            content: Vec::from(content),
        }
    }

    pub fn new_call(topic: &str, method: &str, args: &[u8]) -> (Self, Receiver<CallReturn>) {
        let (tx, rx) = sync_channel(1);
        let call_msg = Box::new(CallMessage {
            method: method.to_string(),
            args: Vec::from(args),
            sender: tx,
        });
        let mut data: Vec<u8> = Vec::new();
        data.extend_from_slice(CALL_MAGIC);
        let addr = Box::into_raw(call_msg);
        // infoln!("call_msg addr {:p}", addr);
        let addr_val = addr as usize;
        let addr_slice =
            unsafe { from_raw_parts(&addr_val as *const usize as *const u8, size_of::<usize>()) };
        data.extend(addr_slice);
        (
            Message {
                topic: topic.to_string(),
                content: data,
            },
            rx,
        )
    }

    pub fn topic(&self) -> &str {
        self.topic.as_str()
    }

    pub fn content(&self) -> &[u8] {
        self.content.as_slice()
    }

    pub fn handle_call_data<F>(&self, f: F, send_result: bool) -> bool
    where
        F: FnOnce(&str, &str, &[u8]) -> CallReturn,
    {
        if let Some(p) = self.call_data() {
            let call_msg = Box::leak(unsafe { Box::from_raw(p) });
            let result = f(
                self.topic.as_str(),
                call_msg.method.as_str(),
                call_msg.args.as_slice(),
            );
            if send_result {
                // infoln!("{} send call response, CallMessage {:p}", send_result, p);
                call_msg.sender.send(result).map_err(|e| {
                    errorln!("call send response failed: {}", e);
                });
            }
            return true;
        }
        false
    }

    fn is_call(&self) -> bool {
        if self.content.len() > 4 {
            let data = &self.content.as_slice()[0..4];
            if data == CALL_MAGIC {
                return true;
            }
        }
        return false;
    }

    fn call_data(&self) -> Option<*mut CallMessage> {
        if self.content.len() > 4 {
            let data = &self.content.as_slice()[0..4];
            if data == CALL_MAGIC {
                unsafe {
                    let addr = (&self.content.as_slice()[4..]).as_ptr() as *const usize;
                    let p = (*addr) as *mut CallMessage;
                    // println!("call_data {:p}", p);
                    return Some(p);
                }
            }
        }
        None
    }
}

pub struct CallMessage {
    pub method: String,
    pub args: Vec<u8>,
    pub sender: SyncSender<CallReturn>,
}

const DATA_TYPE_UNKNOWN: u8 = 0;
const DATA_TYPE_STR: u8 = 1;
const DATA_TYPE_U32: u8 = 2;
const DATA_TYPE_U64: u8 = 3;
const DATA_TYPE_U16: u8 = 4;
const DATA_TYPE_U8: u8 = 5;
const DATA_TYPE_F32: u8 = 6;
const DATA_TYPE_F64: u8 = 7;

pub struct Packer {
    data: Vec<u8>,
}

macro_rules! put_integer {
    ($self:expr, $v: expr, $type_:expr) => {
        $self.data.extend_from_slice(&[$type_]);
        $self.write_len(size_of_val(&$v) as u32);
        let data =
            unsafe { core::slice::from_raw_parts(&$v as *const _ as *const u8, size_of_val(&$v)) };
        $self.data.extend_from_slice(data);
        $self.data.as_mut_slice()[0] += 1;
    };
}

impl Packer {
    pub fn new() -> Self {
        let mut data = Vec::new();
        data.push(0);
        Self { data }
    }
    pub fn build(self) -> Vec<u8> {
        self.data
    }
    fn write_len(&mut self, s: u32) {
        let buff =
            unsafe { core::slice::from_raw_parts(&s as *const u32 as *const u8, size_of_val(&s)) };
        self.data.extend_from_slice(buff);
    }
    pub fn put_bytes<T: AsRef<[u8]>>(&mut self, s: T) -> &mut Self {
        self.data.extend_from_slice(&[DATA_TYPE_STR]);
        self.write_len(s.as_ref().len() as u32);
        self.data.extend_from_slice(s.as_ref());
        self.data.as_mut_slice()[0] += 1;
        self
    }
    pub fn put_u32(&mut self, v: u32) -> &mut Self {
        put_integer!(self, v, DATA_TYPE_U32);
        self
    }
    pub fn put_u64(&mut self, v: u64) -> &mut Self {
        put_integer!(self, v, DATA_TYPE_U64);
        self
    }
    pub fn put_u16(&mut self, v: u16) -> &mut Self {
        put_integer!(self, v, DATA_TYPE_U16);
        self
    }
    pub fn put_u8(&mut self, v: u8) -> &mut Self {
        put_integer!(self, v, DATA_TYPE_U8);
        self
    }
    pub fn put_f32(&mut self, v: f32) -> &mut Self {
        put_integer!(self, v, DATA_TYPE_F32);
        self
    }
    pub fn put_f64(&mut self, v: f64) -> &mut Self {
        put_integer!(self, v, DATA_TYPE_F64);
        self
    }
}

pub struct Unpacker<'a> {
    data: &'a [u8],
    pos: usize,
}

macro_rules! get_integer {
    ($self:expr, $val_type: tt, $type_:expr) => {
        assert!($self.pos < $self.data.len());
        let type_ = $self.get_type();
        assert!(type_ == $type_);
        $self.pos += 1;
        let len = $self.get_len() as usize;
        let mut v: $val_type = $val_type::default();
        assert!(len as usize == size_of_val(&v));
        let data = &$self.data[$self.pos..$self.pos + len];
        unsafe {
            core::ptr::copy(data.as_ptr(), &mut v as *mut _ as *mut u8, len);
        }
        $self.pos += len;
        return v;
    };
}

impl<'a> Unpacker<'a> {
    pub fn new(s: &'a [u8]) -> Self {
        Self { data: s, pos: 0 }
    }

    pub fn fields(&self) -> u8 {
        assert!(self.data.len() > 0);
        self.data[0]
    }

    fn get_type(&mut self) -> u8 {
        assert!(self.pos <= self.data.len());
        let v = self.data[self.pos];
        self.pos += 1;
        v
    }

    fn get_len(&mut self) -> u32 {
        assert!(self.pos + 4 <= self.data.len());
        let len = self.data[self.pos] as u32
            + (self.data[self.pos + 1] as u32 >> 8)
            + (self.data[self.pos + 2] as u32 >> 16)
            + (self.data[self.pos + 3] as u32 >> 24);
        self.pos += 4;
        len
    }

    pub fn get_bytes(&mut self) -> &'a [u8] {
        assert!(self.pos < self.data.len());
        let type_ = self.data[self.pos];
        assert!(type_ == DATA_TYPE_STR);
        self.pos += 1;
        let len = self.get_len() as usize;
        let data = &self.data[self.pos..self.pos + len];
        self.pos += len as usize;
        data
    }

    pub fn get_u32(&mut self) -> u32 {
        get_integer!(self, u32, DATA_TYPE_U32);
    }

    pub fn get_u64(&mut self) -> u64 {
        get_integer!(self, u64, DATA_TYPE_U64);
    }
    pub fn get_u16(&mut self) -> u16 {
        get_integer!(self, u16, DATA_TYPE_U16);
    }
    pub fn get_u8(&mut self) -> u8 {
        get_integer!(self, u8, DATA_TYPE_U8);
    }
    pub fn get_f32(&mut self) -> f32 {
        get_integer!(self, f32, DATA_TYPE_F32);
    }
    pub fn get_f64(&mut self) -> f64 {
        get_integer!(self, f64, DATA_TYPE_F64);
    }
}
